package com.welson.restfultoolx.service;

import com.intellij.openapi.components.Service;
import com.welson.restfultoolx.model.ApiInfo;
import com.welson.restfultoolx.model.SearchOptions;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 搜索引擎服务实现
 * 提供高性能的模糊匹配和实时过滤功能
 */
@Service
public final class SearchEngineService implements SearchEngine {
    
    private final Map<String, ApiInfo> apiIndex = new ConcurrentHashMap<>();
    private final Map<String, Set<String>> pathIndex = new ConcurrentHashMap<>();
    private final Map<String, Set<String>> methodIndex = new ConcurrentHashMap<>();
    private final Map<String, Set<String>> classIndex = new ConcurrentHashMap<>();
    private final Set<String> allPaths = ConcurrentHashMap.newKeySet();
    private final Set<String> allMethods = ConcurrentHashMap.newKeySet();
    private final Set<String> allClasses = ConcurrentHashMap.newKeySet();
    
    @Override
    public List<ApiInfo> search(String query, SearchOptions options) {
        if (query == null || query.trim().isEmpty()) {
            return Collections.emptyList();
        }
        
        query = query.trim();
        if (!options.isCaseSensitive()) {
            query = query.toLowerCase();
        }
        
        Set<String> matchedIds = new HashSet<>();
        
        // 在不同字段中搜索
        if (options.isSearchInPath()) {
            matchedIds.addAll(searchInIndex(pathIndex, query, options));
        }
        
        if (options.isSearchInMethodName()) {
            matchedIds.addAll(searchInIndex(methodIndex, query, options));
        }
        
        if (options.isSearchInClassName()) {
            matchedIds.addAll(searchInIndex(classIndex, query, options));
        }
        
        // 获取匹配的API信息
        List<ApiInfo> results = matchedIds.stream()
                .map(apiIndex::get)
                .filter(Objects::nonNull)
                .filter(api -> filterByOptions(api, options))
                .collect(Collectors.toList());
        
        // 排序结果
        sortResults(results, query, options);
        
        // 限制结果数量
        int maxResults = options.getMaxResults();
        if (results.size() > maxResults) {
            results = results.subList(0, maxResults);
        }
        
        return results;
    }
    
    private Set<String> searchInIndex(Map<String, Set<String>> index, String query, SearchOptions options) {
        Set<String> matchedIds = new HashSet<>();
        
        for (Map.Entry<String, Set<String>> entry : index.entrySet()) {
            String indexKey = entry.getKey();
            if (!options.isCaseSensitive()) {
                indexKey = indexKey.toLowerCase();
            }
            
            if (isMatch(indexKey, query, options)) {
                matchedIds.addAll(entry.getValue());
            }
        }
        
        return matchedIds;
    }
    
    private boolean isMatch(String text, String query, SearchOptions options) {
        if (options.isExactMatch()) {
            return text.equals(query);
        }
        
        // 主要匹配方式：包含匹配（不区分大小写）
        if (text.contains(query)) {
            return true;
        }
        
        // 支持通配符匹配
        if (query.contains("*") || query.contains("?")) {
            String regex = query.replace("*", ".*").replace("?", ".");
            try {
                return Pattern.matches(regex, text);
            } catch (Exception e) {
                return false;
            }
        }
        
        // 只有在查询字符串看起来像驼峰模式时才使用驼峰匹配
        // 例如："getUserInfo" -> "gui" 或 "API" -> "api"
        if (isLikelyCamelCaseQuery(query)) {
            return improvedCamelCaseMatch(text, query);
        }
        
        return false;
    }
    
    // 判断是否可能是驼峰查询模式
    private boolean isLikelyCamelCaseQuery(String query) {
    // 如果查询字符串很短（2-4个字符）且全是字母，可能是驼峰缩写
    if (query.length() >= 2 && query.length() <= 4 && query.matches("[a-zA-Z]+")) {
        return true;
    }
    // 如果包含大写字母，可能是驼峰模式
    return query.matches(".*[A-Z].*");
    }
    
    // 改进的驼峰匹配算法
    private boolean improvedCamelCaseMatch(String text, String query) {
        if (query.length() > text.length()) {
            return false;
        }
        
        // 更严格的驼峰匹配：只匹配单词边界或大写字母开头的位置
        int queryIndex = 0;
        boolean lastWasMatch = false;
        
        for (int i = 0; i < text.length() && queryIndex < query.length(); i++) {
            char textChar = text.charAt(i);
            char queryChar = query.charAt(queryIndex);
            
            if (Character.toLowerCase(textChar) == Character.toLowerCase(queryChar)) {
                queryIndex++;
                lastWasMatch = true;
            } else if (Character.isUpperCase(textChar) && !lastWasMatch) {
            // 只在大写字母且上一个字符不匹配时重新开始
            if (Character.toLowerCase(textChar) == Character.toLowerCase(query.charAt(0))) {
                queryIndex = 1;
                lastWasMatch = true;
            } else {
                lastWasMatch = false;
            }
        } else {
            lastWasMatch = false;
        }
    }
    
    return queryIndex == query.length();
    }
    
    private boolean filterByOptions(ApiInfo api, SearchOptions options) {
        // HTTP方法过滤
        if (options.hasHttpMethodFilter() && !options.isHttpMethodFiltered(api.getHttpMethod())) {
            return false;
        }
        
        // 是否包含已废弃的接口
        if (!options.isIncludeDeprecated() && isDeprecated(api)) {
            return false;
        }
        
        return true;
    }
    
    private boolean isDeprecated(ApiInfo api) {
        return api.getAnnotations() != null && 
               api.getAnnotations().stream().anyMatch(annotation -> 
                   annotation.contains("Deprecated"));
    }
    
    private void sortResults(List<ApiInfo> results, String query, SearchOptions options) {
        String sortBy = options.getSortBy();
        boolean ascending = options.isAscending();
        
        Comparator<ApiInfo> comparator;
        
        switch (sortBy.toLowerCase()) {
            case "path":
                comparator = Comparator.comparing(ApiInfo::getPath);
                break;
            case "method":
                comparator = Comparator.comparing(ApiInfo::getHttpMethod);
                break;
            case "class":
                comparator = Comparator.comparing(ApiInfo::getClassName);
                break;
            case "relevance":
            default:
                comparator = (a1, a2) -> calculateRelevance(a2, query) - calculateRelevance(a1, query);
                ascending = false; // 相关性总是降序
                break;
        }
        
        if (!ascending) {
            comparator = comparator.reversed();
        }
        
        results.sort(comparator);
    }
    
    private int calculateRelevance(ApiInfo api, String query) {
        int score = 0;
        String lowerQuery = query.toLowerCase();
        
        // 路径匹配得分
        String path = api.getPath().toLowerCase();
        if (path.equals(lowerQuery)) {
            score += 100;
        } else if (path.startsWith(lowerQuery)) {
            score += 80;
        } else if (path.contains(lowerQuery)) {
            score += 60;
        }
        
        // 方法名匹配得分
        String methodName = api.getMethodName().toLowerCase();
        if (methodName.equals(lowerQuery)) {
            score += 90;
        } else if (methodName.startsWith(lowerQuery)) {
            score += 70;
        } else if (methodName.contains(lowerQuery)) {
            score += 50;
        }
        
        // 类名匹配得分
        String className = api.getSimpleClassName().toLowerCase();
        if (className.equals(lowerQuery)) {
            score += 80;
        } else if (className.startsWith(lowerQuery)) {
            score += 60;
        } else if (className.contains(lowerQuery)) {
            score += 40;
        }
        
        // HTTP方法匹配得分
        if (api.getHttpMethod().toLowerCase().equals(lowerQuery)) {
            score += 30;
        }
        
        return score;
    }
    
    @Override
    public void buildIndex(List<ApiInfo> apis) {
        clearIndex();
        
        for (ApiInfo api : apis) {
            if (api != null) {
                addToIndex(api);
            }
        }
    }
    
    private void addToIndex(ApiInfo api) {
        String apiId = api.getId();
        if (apiId == null) {
            apiId = generateApiId(api);
            api.setId(apiId);
        }
        
        // 添加到主索引
        apiIndex.put(apiId, api);
        
        // 添加到路径索引
        if (api.getPath() != null) {
            pathIndex.computeIfAbsent(api.getPath(), k -> ConcurrentHashMap.newKeySet()).add(apiId);
            allPaths.add(api.getPath());
        }
        
        // 添加到方法名索引
        if (api.getMethodName() != null) {
            methodIndex.computeIfAbsent(api.getMethodName(), k -> ConcurrentHashMap.newKeySet()).add(apiId);
            allMethods.add(api.getMethodName());
        }
        
        // 添加到类名索引
        if (api.getClassName() != null) {
            String simpleClassName = api.getSimpleClassName();
            classIndex.computeIfAbsent(simpleClassName, k -> ConcurrentHashMap.newKeySet()).add(apiId);
            allClasses.add(simpleClassName);
        }
    }
    
    private String generateApiId(ApiInfo api) {
        return String.format("%s#%s#%s#%s", 
            api.getClassName(), 
            api.getMethodName(), 
            api.getHttpMethod(), 
            api.getPath());
    }
    
    @Override
    public void updateIndex(ApiInfo api, IndexOperation operation) {
        switch (operation) {
            case ADD:
            case UPDATE:
                addToIndex(api);
                break;
            case REMOVE:
                removeFromIndex(api);
                break;
        }
    }
    
    private void removeFromIndex(ApiInfo api) {
        String apiId = api.getId();
        if (apiId == null) {
            return;
        }
        
        // 从主索引移除
        apiIndex.remove(apiId);
        
        // 从各个索引中移除
        removeFromSubIndex(pathIndex, api.getPath(), apiId);
        removeFromSubIndex(methodIndex, api.getMethodName(), apiId);
        removeFromSubIndex(classIndex, api.getSimpleClassName(), apiId);
    }
    
    private void removeFromSubIndex(Map<String, Set<String>> index, String key, String apiId) {
        if (key != null) {
            Set<String> ids = index.get(key);
            if (ids != null) {
                ids.remove(apiId);
                if (ids.isEmpty()) {
                    index.remove(key);
                }
            }
        }
    }
    
    @Override
    public void clearIndex() {
        apiIndex.clear();
        pathIndex.clear();
        methodIndex.clear();
        classIndex.clear();
        allPaths.clear();
        allMethods.clear();
        allClasses.clear();
    }
    
    @Override
    public int getIndexSize() {
        return apiIndex.size();
    }
    
    @Override
    public boolean isIndexEmpty() {
        return apiIndex.isEmpty();
    }
    
    @Override
    public List<String> getSuggestions(String query, int maxSuggestions) {
        if (query == null || query.trim().isEmpty()) {
            return Collections.emptyList();
        }
        
        query = query.toLowerCase();
        Set<String> suggestions = new HashSet<>();
        
        // 从路径中获取建议
        addSuggestions(allPaths, query, suggestions, maxSuggestions);
        
        // 从方法名中获取建议
        if (suggestions.size() < maxSuggestions) {
            addSuggestions(allMethods, query, suggestions, maxSuggestions);
        }
        
        // 从类名中获取建议
        if (suggestions.size() < maxSuggestions) {
            addSuggestions(allClasses, query, suggestions, maxSuggestions);
        }
        
        return new ArrayList<>(suggestions).subList(0, Math.min(suggestions.size(), maxSuggestions));
    }
    
    private void addSuggestions(Set<String> source, String query, Set<String> suggestions, int maxSuggestions) {
        for (String item : source) {
            if (suggestions.size() >= maxSuggestions) {
                break;
            }
            
            if (item.toLowerCase().contains(query)) {
                suggestions.add(item);
            }
        }
    }
}